Next.js on Cloudflare Pages
from 2024-12-22
Get started | Full-stack (SSR) | Next.js apps · Cloudflare Pages docs
bun create cloudflare@latest poc-next-cf --framework=next
なんか適当にデプロイまで終わるの感動的だな
bun dev 動かないんだけど
% bun dev
$ next dev
/Users/funwarioisii/ghq/github.com/xxx/poc-next-cf/next.config.compiled.js:16
await (0, _nextdev.setupDevPlatform)();
^
ReferenceError: await is not defined
default の next.config.ts が悪そう
code:next.config.mjs
import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev';
if (process.env.NODE_ENV === 'development') {
await setupDevPlatform();
}
/** @type {import('next').NextConfig} */
const nextConfig = {
/* config options here */
};
export default nextConfig;
としたらよくなった
next/image がどう動いてるのかよくわからない
d1を入れていく
Cloudflare D1 · Cloudflare D1 docs
D1 is designed for horizontal scale out across multiple, smaller (10 GB) databases, such as per-user, per-tenant or per-entity databases. D1 allows you to build applications with thousands of databases at no extra cost for isolating with multiple databases. D1 pricing is based only on query and storage costs.
テーブルの設計や実装にクセがありそうだ
D1の構築:グローバルデータベース
各地にあるd1はリードレプリカなので、商品に限りがある場合の購入などはDBを分けたほうがよさそうだ?
リードレプリカを使用するサーバベースのデータベースでは、セッション内のすべてのクエリで同じサーバを使用することが重要です。 同じセッションで別のリードレプリカに切り替えると、アプリケーションが提供する一貫性モデルが損なわれて、データベースの動作に関する仮定に違反し、アプリケーションが正しくない結果を返す場合があります!
通知みたいなものを SQLite脳で考えると users.sqlite のread replica users-rr1.sqlite と users-rr2.sqlite があって、notifications.sqlite と -rr1/2.sqlite があって、それぞれ -rr1 には 10ms, -rr2 には 100ms 遅れで replication されるとすると、users-rr1 で見て noti-rr2 で見るとズレるとかそういう
雑に書いてるからなんの例証にもなってないw
sqldef to d1
code:bash
#!/bin/bash
echo 'input migration name'
read input
SCRIPT_DIR=$(cd $(dirname $0); pwd)
sqlite3def --dry-run ${SCRIPT_DIR}/../.wrangler/state/d1/DB.sqlite3 < ${SCRIPT_DIR}/../database/schema.sql > ${SCRIPT_DIR}/../migrations/date +%Y%m%d%H%M%S_${input}.sql
pnpm wrangler d1 migrations apply --local mydb
workers-remix-d1/scripts/migration.sh at main · chimame/workers-remix-d1 · GitHub
本番デプロイしようとすると失敗する
code:log
✘ ERROR Deployment failed!
Failed to publish your Function. Got error: binding DB of type
d1 must have a database that already exists. Use wrangler or
the UI to create the database.
code:toml
d1_databases
binding = "DB"
database_name="..."
database_id="..."
と書いているんだけど、UIで確認すると binding できていなさそう
諦めて再度 wrangler d1 create <database> した
Server Actionsが出来るか
useActionStateを使っている
code:typescript
'use client'
import { useActionState } from 'react';
import { incrementAction } from './action';
const initialState = {
error: "",
success: undefined,
};
export default function ExamplePage() {
const state, formAction, isPending = useActionState(incrementAction, initialState);
return (
<div>
<p>{state.error}</p>
<form action={formAction}>
<button type="submit">Increment</button>
</form>
</div>
);
}
code:action.ts
'use server'
import { getDB } from '@/lib/db';
export async function incrementAction(_prev: any, formData: FormData): Promise<{
error?: string;
success?: string;
}> {
const id = 'e04c540e38245df89bfe7ee99f04ff97';
const db = getDB();
const query = "update counts set count = count + 1 where id = ?1";
const result = await db.prepare(query).bind(id).run();
return {
success: 'ok'
};
}
code:db/index.ts
function getDB(): D1Database {
const db = process.env.DB;
if (!db) throw new Error('DB is not set');
return db;
}
export { getDB };
これは getDB で失敗する
export const runtime = 'edge'; を各ページでつけて回る必要がありそうだ